/*
 * Copyright (c) Kumo Inc. and affiliates.
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <melon/executor.h>

#include <stdexcept>

#include <turbo/log/logging.h>

#include <melon/exception_string.h>
#include <melon/portability.h>
#include <melon/lang/exception.h>

namespace melon {
    void Executor::invokeCatchingExnsLog(char const *const prefix) noexcept {
        auto ep = current_exception();
        KLOG(ERROR) << prefix << " threw unhandled " << exceptionStr(ep);
    }

    void Executor::addWithPriority(Func, int8_t /* priority */) {
        throw std::runtime_error(
            "addWithPriority() is not implemented for this Executor");
    }

    bool Executor::keepAliveAcquire() noexcept {
        return false;
    }

    void Executor::keepAliveRelease() noexcept {
        KLOG(FATAL) << __func__ << "() should not be called for melon::Executor types "
                << "which do not override keepAliveAcquire()";
    }

    // Base case of permitting with no termination to avoid nullptr tests
    static ExecutorBlockingList emptyList{nullptr, {false, false, nullptr, {}}};

    thread_local ExecutorBlockingList *executor_blocking_list = &emptyList;

    Optional<ExecutorBlockingContext> getExecutorBlockingContext() noexcept {
        return //
                kIsMobile || !executor_blocking_list->curr.forbid
                    ? none
                    : //
                    make_optional(executor_blocking_list->curr);
    }

    ExecutorBlockingGuard::ExecutorBlockingGuard(PermitTag) noexcept {
        if (!kIsMobile) {
            list_ = *executor_blocking_list;
            list_.prev = executor_blocking_list;
            list_.curr.forbid = false;
            // Do not overwrite tag or executor pointer
            executor_blocking_list = &list_;
        }
    }

    ExecutorBlockingGuard::ExecutorBlockingGuard(
        TrackTag, Executor *ex, StringPiece tag) noexcept {
        if (!kIsMobile) {
            list_ = *executor_blocking_list;
            list_.prev = executor_blocking_list;
            list_.curr.forbid = true;
            list_.curr.ex = ex;
            // If no string was provided, maintain the parent string to keep some
            // information
            if (!tag.empty()) {
                list_.curr.tag = tag;
            }
            executor_blocking_list = &list_;
        }
    }

    ExecutorBlockingGuard::ExecutorBlockingGuard(
        ProhibitTag, Executor *ex, StringPiece tag) noexcept {
        if (!kIsMobile) {
            list_ = *executor_blocking_list;
            list_.prev = executor_blocking_list;
            list_.curr.forbid = true;
            list_.curr.ex = ex;
            list_.curr.allowTerminationOnBlocking = true;
            // If no string was provided, maintain the parent string to keep some
            // information
            if (!tag.empty()) {
                list_.curr.tag = tag;
            }
            executor_blocking_list = &list_;
        }
    }

    ExecutorBlockingGuard::~ExecutorBlockingGuard() {
        if (!kIsMobile) {
            if (executor_blocking_list != &list_) {
                terminate_with<std::logic_error>("dtor mismatch");
            }
            executor_blocking_list = list_.prev;
        }
    }
} // namespace melon
